#include "StdAfx.h"
#include "Trainer.h"
#include "../NeuralBasic/Network.h"
#include "../MnistDigit/Digit.h"

namespace Recognition
{

	Trainer::Trainer(NeuralBasic::Network& network
		, std::vector<std::unique_ptr<MnistDigit::Digit>>& digits)
		: m_network(network)
		, m_digits(digits)
		, m_itemError(10)//TODO: remove 10, 1.0 1 etc
		, m_isStopped(true)
	{
	}

	Trainer::~Trainer(void)
	{
	}

	void Trainer::Stop()
	{
		m_isStopped = true;
	}

	void Trainer::Start()
	{
		m_isStopped = false;
		const size_t epochCount = 100;
		for(size_t epoch=0; epoch<epochCount && !m_isStopped; ++epoch)
			Epoch();
	}

	void Trainer::Epoch()
	{
		for(auto it=m_digits.begin(), itEnd=m_digits.end()
			;it!=itEnd && !m_isStopped
			;++it)
		{
			Item(*it);
		}
	}

	void Trainer::CalculateError(const std::vector<double>& error)
	{
		auto it = std::max_element(error.begin(), error.end()
			,[](const double left, const double right)
		{
			return abs(left)<abs(right);
		}
		);
		m_error = abs(*it);
	}

	double Trainer::GetError() const
	{
		return m_error;
	}

	void Trainer::Item(const std::unique_ptr<MnistDigit::Digit>& digit)
	{
		const std::vector<double>& input = digit->GetInput();
		const std::vector<double>& output = m_network.Work(input);
		const std::vector<double>& desired = digit->GetDesired();

		std::transform(desired.begin(), desired.end()
			, output.begin()
			, m_itemError.begin()
			, std::minus<double>());

		CalculateError(m_itemError);

		m_network.Train(input, m_itemError);
	}

}